Decorators


ONLY AFTER FP

A decorator is the name used for a software design pattern. Decorators dynamically alter the functionality of a function, method, or class without having to directly use subclasses or change the source code of the function being decorated.

Python decorator is a specific change to the Python syntax that allows us to more conveniently alter functions and methods (and possibly classes in a future version). This supports more readable applications of the DecoratorPattern but also other uses as well.


In [ ]:
# sandwich()
# test = bread(ingredients(Cheese))
# test()

# # bread_1 = ingredients(Cheese)
# # print(bread_1)
# # bread_1()

In [7]:
def bread(test_funct):
    def hyderabad():
        print("</''''''\>")
        test_funct()
        print("<\______/>")
    return hyderabad

def ingredients(test_funct):
    def chennai():
        print("#tomatoes#")
        test_funct()
        print("~salad~")
    return chennai

def cheese(food="--Say Cheese--"):
    print(food)

In [8]:
ch = bread(cheese)

inn = bread(ingredients(cheese))

ch()
print("^"*20)
inn()


</''''''\>
--Say Cheese--
<\______/>
^^^^^^^^^^^^^^^^^^^^
</''''''\>
#tomatoes#
--Say Cheese--
~salad~
<\______/>

In [9]:
@bread
@ingredients
def sandwich(food="--Say Cheese--"):
    print(food)

sandwich()


</''''''\>
#tomatoes#
--Say Cheese--
~salad~
<\______/>

!!! Order Matters !!!


In [10]:
@ingredients
@bread
def sandwich(food="--Say Cheese--"):
    print(food)

sandwich()


#tomatoes#
</''''''\>
--Say Cheese--
<\______/>
~salad~

In [11]:
@bread
@ingredients
def hotdog(food="Jam"):
    print(food)

hotdog()


</''''''\>
#tomatoes#
Jam
~salad~
<\______/>

In [12]:
def Names(test_funct):
    def inner():
        print("{Hello}")
        print("\tA-Priya")
        print("\tManish Gupta")
        print("\tNeha", end="\n\t")
        test_funct()
        print("(/Hello}")
    return inner

@Names
def print_AShanti():
    print("A-Shanti")

print_AShanti()


{Hello}
	A-Priya
	Manish Gupta
	Neha
	A-Shanti
(/Hello}

In [10]:
def names(test_funct):
    def inner():
        print("\t", name)
        test_funct()
    return inner()

def Hello(test_funct):
    def inner():
        print("{Hello}")
        print("\tA-Priya")
        print("\tManish Gupta")
        print("\tNeha", end="\n\t")
        test_funct()
        print("(/Hello}")
    return inner

@Hello
@names(name="Mayank")
def print_AShanti():
    print("A-Shanti")

print_AShanti()


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-10-90733c8b1b9d> in <module>()
     16 
     17 @Hello
---> 18 @names(name="Mayank")
     19 def print_AShanti():
     20     print("A-Shanti")

TypeError: names() got an unexpected keyword argument 'name'

Bound methods


Unless you tell it not to, Python will create what is called a bound method when a function is an attribute of a class and you access it via an instance of a class. This may sound complicated but it does exactly what you want.


In [22]:
class A(object):
    def method(*argv):
        return argv
a = A()
a.method


Out[22]:
<bound method A.method of <__main__.A object at 0x00000234D70DB8D0>>

In [23]:
a.method('an arg')


Out[23]:
(<__main__.A at 0x234d70db8d0>, 'an arg')

staticmethod()

A static method is a way of suppressing the creation of a bound method when accessing a function.


In [24]:
class A(object):
    @staticmethod
    def method(*argv):
        return argv
a = A()
a.method


Out[24]:
<function __main__.A.method>

When we call a static method we don’t get any additional arguments.


In [25]:
a.method('an arg')


Out[25]:
('an arg',)

classmethod

A class method is like a bound method except that the class of the instance is passed as an argument rather than the instance itself.


In [3]:
class A(object):
    @classmethod
    def method(*argv):
        return argv
a = A()
a.method


Out[3]:
<bound method type.method of <class '__main__.A'>>

In [2]:
a.method('an arg')


Out[2]:
(__main__.A, 'an arg')

In [3]:
def test(strg):
    print("Name: ", strg)
    
def hello(func, name):
    print("Ja")
    func(name)
    
hello(test, "Mayank")


Ja
Name:  Mayank

In [4]:
class B(object):
    @classmethod
    def method(*argv):
        return argv

In [5]:
a = B()
a.method()


Out[5]:
(__main__.B,)